package com.atguigu.daijia.coupon.service.impl;

import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.coupon.mapper.CouponInfoMapper;
import com.atguigu.daijia.coupon.mapper.CustomerCouponMapper;
import com.atguigu.daijia.coupon.service.CouponInfoService;
import com.atguigu.daijia.model.entity.coupon.CouponInfo;
import com.atguigu.daijia.model.entity.coupon.CustomerCoupon;
import com.atguigu.daijia.model.form.coupon.UseCouponForm;
import com.atguigu.daijia.model.vo.base.PageVo;
import com.atguigu.daijia.model.vo.coupon.AvailableCouponVo;
import com.atguigu.daijia.model.vo.coupon.NoReceiveCouponVo;
import com.atguigu.daijia.model.vo.coupon.NoUseCouponVo;
import com.atguigu.daijia.model.vo.coupon.UsedCouponVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 谢忠涵7
 */
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class CouponInfoServiceImpl extends ServiceImpl<CouponInfoMapper, CouponInfo> implements CouponInfoService {

    @Autowired
    private CouponInfoMapper couponInfoMapper;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private CustomerCouponMapper customerCouponMapper;
    /**
     * 查询未领取优惠券分页列表
     */
    @Override
    public PageVo<NoReceiveCouponVo> findNoReceivePage(Page<CouponInfo> pageParam, Long customerId) {
        IPage<NoReceiveCouponVo> pageInfo = couponInfoMapper.findNoReceivePage(pageParam, customerId);
        return new PageVo<>(pageInfo.getRecords(),pageInfo.getPages(), pageInfo.getTotal());
    }

    /**
     * 查询未使用优惠券分页列表
     */
    @Override
    public PageVo<NoUseCouponVo> findNoUsePage(Page<CouponInfo> pageParam, Long customerId) {
        IPage<NoUseCouponVo> pageInfo = couponInfoMapper.findNoUsePage(pageParam, customerId);
        return new PageVo(pageInfo.getRecords(), pageInfo.getPages(), pageInfo.getTotal());
    }

    /**
     * 查询已使用优惠券分页列表
     */
    @Override
    public PageVo<UsedCouponVo> findUsedPage(Page<CouponInfo> pageParam, Long customerId) {
        IPage<UsedCouponVo> pageInfo = couponInfoMapper.findUsedPage(pageParam, customerId);
        return new PageVo(pageInfo.getRecords(), pageInfo.getPages(), pageInfo.getTotal());
    }

    /**
     * 领取优惠券
     */
    @Override
    public Boolean receive(Long customerId, Long couponId) {
        //1.首先要先根据couponId查询该优惠劵是否存在
        CouponInfo couponInfo = couponInfoMapper.selectById(couponId);
        if(null == couponInfo){
            throw new GuiguException(ResultCodeEnum.COUPON_NO_EXIST);
        }
        //2.查询优惠劵是否过期
        if(couponInfo.getExpireTime().before(new Date())){
            throw new GuiguException(ResultCodeEnum.COUPON_EXPIRE);
        }
        //3.检查库存，发行数量 和 领取数量
        if(couponInfo.getPublishCount() != 0 &&
                Objects.equals(couponInfo.getReceiveCount(), couponInfo.getPublishCount())) {
            throw new GuiguException(ResultCodeEnum.COUPON_LESS);
        }
        //分布式锁 使用 redisson 来加锁
        RLock lock = null;
        try {
            //上锁
             lock = redissonClient.getLock(RedisConstant.COUPON_LOCK + customerId);
            boolean tryLock = lock.tryLock(RedisConstant.COUPON_LOCK_WAIT_TIME,
                    RedisConstant.COUPON_LOCK_LEASE_TIME, TimeUnit.SECONDS);
            if(tryLock) {
                //4.查询每个人的领取限制
                // 先判断每个人的领取限制是否为0(无限制)
                if(couponInfo.getPerLimit() > 0) {
                    //查询已领取的优惠劵的数量
                    LambdaQueryWrapper<CustomerCoupon> wrapper = new LambdaQueryWrapper<>();
                    wrapper.eq(CustomerCoupon::getCustomerId, customerId);
                    wrapper.eq(CustomerCoupon::getCouponId, couponId);
                    Long count = customerCouponMapper.selectCount(wrapper);
                    if(count == couponInfo.getPerLimit().longValue()) {
                        throw new GuiguException(ResultCodeEnum.COUPON_USER_LIMIT);
                    }
                }
                //5 领取优惠卷
                //5.1 更新领取数量
                int row = couponInfoMapper.updateReceiveCount(couponId);
                if(row == 1) {
                    //5.2 添加领取记录
                    this.saveCustomerCoupon(customerId,couponId,couponInfo.getExpireTime());
                    return true;
                }else {
                    throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
                }
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(lock != null) {
                lock.unlock();
            }
        }
        throw new GuiguException(ResultCodeEnum.COUPON_LESS);
    }

    /**
     * 获取未使用的最佳优惠券信息
     */
    @Override
    public List<AvailableCouponVo> findAvailableCoupon(Long customerId, BigDecimal orderAmount) {
        //1 创建list集合，存储最终返回数据
        List<AvailableCouponVo> availableCouponVoList = new ArrayList<>();
        //1.先获取到未使用的已领取的优惠劵列表
        List<NoUseCouponVo> list  = couponInfoMapper.findNoUseList(customerId);
        //2.分为两个类型的优惠劵
        //现金列表
        List<NoUseCouponVo> moneyList = list.stream().filter(item -> item.getCouponType() == 1).toList();
        //折扣列表
        List<NoUseCouponVo> discountList = list.stream().filter(item -> item.getCouponType() == 2).toList();
        //3.折扣劵
       for(NoUseCouponVo item : discountList) {
           //先计算出折扣后的金额为
           BigDecimal discountAmount = orderAmount.multiply(item.getDiscount()).divide(new BigDecimal("10")
                   ,new MathContext(2, RoundingMode.HALF_UP));
           //在计算出减免的金额
           BigDecimal reduceAmount = orderAmount.subtract(discountAmount);
           //有门槛 订单金额要大于门槛金额
           if(item.getConditionAmount().intValue() > 0
                   && orderAmount.subtract(item.getConditionAmount()).intValue() > 0){
               availableCouponVoList.add(this.buildBestNoUseCouponVo(item,reduceAmount));
           }
           //没门槛
           if(item.getConditionAmount().intValue() == 0){
               availableCouponVoList.add(this.buildBestNoUseCouponVo(item,reduceAmount));
           }
       }
        //4.现金劵
        for(NoUseCouponVo item : moneyList){
            BigDecimal reduceAmount = item.getAmount();
            //有门槛 订单金额要大于门槛金额
            if(item.getConditionAmount().intValue() > 0
                    && orderAmount.subtract(item.getConditionAmount()).intValue() > 0) {
                availableCouponVoList.add(this.buildBestNoUseCouponVo(item,reduceAmount));
            }
            //没门槛 订单金额减去现金要大于0
            if(item.getConditionAmount().intValue() == 0 &&
                    orderAmount.subtract(reduceAmount).intValue() > 0) {
                availableCouponVoList.add(this.buildBestNoUseCouponVo(item,reduceAmount));
            }
        }
        //排序按减免金额的从大到小排序
        if (!CollectionUtils.isEmpty(availableCouponVoList)) {
            availableCouponVoList.sort(new Comparator<AvailableCouponVo>() {
                @Override
                public int compare(AvailableCouponVo o1, AvailableCouponVo o2) {
                    return o1.getReduceAmount().compareTo(o2.getReduceAmount());
                }
            });
        }
        return  availableCouponVoList;
    }

    /**
     * 添加领取记录
     */
    private void saveCustomerCoupon(Long customerId, Long couponId, Date expireTime) {
        CustomerCoupon customerCoupon = new CustomerCoupon();
        customerCoupon.setCouponId(couponId);
        customerCoupon.setCustomerId(customerId);
        customerCoupon.setExpireTime(expireTime);
        customerCoupon.setReceiveTime(new Date());
        customerCoupon.setStatus(1);
        customerCouponMapper.insert(customerCoupon);
    }

    /**
     * 封装AvailableCouponVo对象
     */
    private AvailableCouponVo buildBestNoUseCouponVo(NoUseCouponVo noUseCouponVo, BigDecimal reduceAmount) {
        AvailableCouponVo bestNoUseCouponVo = new AvailableCouponVo();
        BeanUtils.copyProperties(noUseCouponVo, bestNoUseCouponVo);
        bestNoUseCouponVo.setCouponId(noUseCouponVo.getId());
        bestNoUseCouponVo.setReduceAmount(reduceAmount);
        return bestNoUseCouponVo;
    }
}
